本系列文已改編成書「Arduino 自造趣:結合 JavaScript x Vue x Phaser 輕鬆打造個人遊戲機」,本書改用 Vue3 與 TypeScript 全面重構且加上更詳細的說明,
在此感謝 iT 邦幫忙、博碩文化與編輯小 p 的協助,歡迎大家前往購書,鱈魚在此感謝大家 (。・∀・)。
若想 DIY 卻不知道零件去哪裡買的讀者,可以參考此連結 。( •̀ ω •́ )✧
接下來我們開始建立「類比輸入視窗」。
和數位訊號只有 0 與 1 兩種狀態不同,類比訊號是連續的訊號,實際上自然界的物理量都屬於類比訊號,例如:聲音、光、電壓等等。
更詳細的定義與說明可以參考以下連結:
wiki - 類比訊號
wiki - 數位訊號
Uno 上的類比輸入指的是「電壓」訊號,Uno 可以讀取 0 至 5V 的電壓並轉換為數位訊號回傳。
具體實現方式為透過 ADC(Analog to Digital Convert)轉換電壓訊號,Uno 使用的轉換器解析度為 10 位元,所以會將 0 至 5V 的電壓轉換為 0 至 1023(2 的 10 次方 - 1)的數值呈現。
將 window-example.vue
複製一份後改個名字,建立 window-analog-input.vue
。
src\components\window-analog-input.vue
<template lang="pug">
base-window.window-analog-input(
:pos='pos',
headerIconColor='red-3',
body-class='c-col p-20px pt-20px',
title='類比輸入功能'
)
</template>
<style lang="sass">
.window-analog-input
width: 300px
height: 400px
</style>
<script>
import BaseWindow from '@/components/base-window.vue';
import mixinWindow from '@/mixins/mixin-window';
export default {
name: 'WindowAnalogInput',
components: {
'base-window': BaseWindow,
},
mixins: [mixinWindow],
props: {},
data() {
return {
id: this.$vnode.key,
};
},
provide() {
return {
id: this.id,
};
},
computed: {},
watch: {},
created() {
console.log(`[ ${this.$options.name} created ] id : `, this.id);
},
mounted() {},
methods: {},
};
</script>
忽然發現其實 data()
中的 id
與 provide()
內容在每個 window 也都重複出現,所以一樣遷移至 mixin-window
中。
src\mixins\mixin-window.js
/**
* 標準 window 共用內容
*/
export default {
props: {
pos: {
type: Object,
default() {
return {
x: 0,
y: 0,
};
},
},
},
data() {
return {
id: this.$vnode.key,
};
},
provide() {
return {
id: this.id,
};
},
}
並將 window-analog-input.vue
、window-digital-io.vue
、window-example.vue
已遷移的部份刪除。
src\components\window-analog-input.vue <script>
import BaseWindow from '@/components/base-window.vue';
import mixinWindow from '@/mixins/mixin-window';
export default {
name: 'WindowAnalogInput',
components: {
'base-window': BaseWindow,
},
mixins: [mixinWindow],
props: {},
data() {
return {};
},
computed: {},
watch: {},
created() {
console.log(`[ ${this.$options.name} created ] id : `, this.id);
},
mounted() {},
methods: {},
};
內容變得更清爽了!
接著回到 app.vue
,將右鍵選單內加入『新增「類比輸入視窗」』選項,並引入組件。
src\app.vue <template lang="pug">
.screen(@click='handleClick')
// ...
// 右鍵選單
q-menu(context-menu, content-class='border-radius-s')
q-list.min-w-260px
q-item(@click='addWindow("window-digital-io")', clickable, v-close-popup)
q-item-section
| 新增「數位 I/O 視窗」
q-item(@click='addWindow("window-analog-input")', clickable, v-close-popup)
q-item-section
| 新增「類比輸入視窗」
src\app.vue <script>
// ...
import WindowDigitalIo from '@/components/window-digital-io.vue';
import WindowAnalogInput from '@/components/window-analog-input.vue';
export default {
name: 'App',
components: {
'dialog-system-setting': DialogSystemSetting,
'window-digital-io': WindowDigitalIo,
'window-analog-input': WindowAnalogInput,
},
// ...
};
「選擇腳位的下拉選單」已經建立完成,直接引入 base-select-pin.vue
。
src\components\window-analog-input.vue <script>
import mixinWindow from '@/mixins/mixin-window';
import BaseWindow from '@/components/base-window.vue';
import BaseSelectPin from '@/components/base-select-pin.vue';
export default {
name: 'WindowAnalogInput',
components: {
'base-window': BaseWindow,
'base-select-pin': BaseSelectPin,
},
mixins: [mixinWindow],
props: {},
data() {
return {};
},
computed: {},
watch: {},
created() {
console.log(`[ ${this.$options.name} created ] id : `, this.id);
},
mounted() {},
methods: {},
};
src\components\window-analog-input.vue <template lang="pug">
base-window.window-analog-input(
:pos='pos',
headerIconColor='red-3',
body-class='c-col p-20px pt-20px',
title='類比輸入功能'
)
base-select-pin(color='red-3')
元件複用的感覺真好 ヾ(◍'౪`◍)ノ゙
接著便是提供腳位清單資料作為 base-select-pin.vue
的 options 顯示,在 computed
增加 supportPins
,提供支援類比功能腳位清單。
src\components\window-analog-input.vue <script>
import { mapState } from 'vuex';
// ...
import { PinMode } from '@/script/utils/firmata.utils';
const { ANALOG_INPUT } = PinMode;
export default {
name: 'WindowAnalogInput',
// ...
computed: {
...mapState({
boardPins: (state) => state.board.info.pins,
}),
// 支援功能的腳位
supportPins() {
/** @type {PinInfo[]} */
const boardPins = this.boardPins;
return boardPins.filter((pin) => {
const hasMode = pin.capabilities.some(
(capability) => ANALOG_INPUT === capability.mode
);
return hasMode;
});
},
},
// ...
};
src\components\window-analog-input.vue <template lang="pug">
base-window.window-analog-input(
:pos='pos',
headerIconColor='red-3',
body-class='c-col p-20px pt-20px',
title='類比輸入功能'
)
base-select-pin(:pins='supportPins', color='red-3')
看看結果如何。
可以看到只剩下 Pin 14 到 Pin 19。
這個部分基本上與「數位 I/O 視窗」相同。
existPins
變數,儲存目前已建立腳位base-select-pin.vue
之 selected
事件,接收被選擇的腳位。src\components\window-analog-input.vue <script>
/**
* @typedef {import('@/types/type').PinInfo} PinInfo
*/
// ...
export default {
name: 'WindowAnalogInput',
// ...
data() {
return {
/** @type {PinInfo[]} */
existPins: [],
};
},
// ...
methods: {
/** 新增腳位
* @param {PinInfo} pin
*/
addPin(pin) {
if (!pin) return;
this.$store.commit('window/addOccupiedPin', {
id: this.id,
pin,
});
this.existPins.push(pin);
},
/** 移除腳位
* @param {PinInfo} pin
*/
deletePin(pin) {
if (!pin) return;
this.$store.commit('window/deleteOccupiedPin', {
id: this.id,
pin,
});
const index = this.existPins.findIndex(
(existPin) => existPin.number === pin.number
);
this.existPins.splice(index, 1);
},
/** 接收錯誤訊息 */
handleErr(msg) {
this.$q.notify({
type: 'negative',
message: msg,
});
},
},
};
src\components\window-analog-input.vue <template lang="pug">
base-window.window-analog-input(
:pos='pos',
headerIconColor='red-3',
body-class='c-col p-20px pt-20px',
title='類比輸入功能'
)
base-select-pin(
:pins='supportPins',
color='red-3',
@selected='addPin',
@err='handleErr'
)
試試看效果如何。
太棒了!再來就是建立類比輸入控制組件,顯示類比輸入數值。
base-select-pin
組件,用於選擇腳位。以上程式碼已同步至 GitLab,大家可以前往下載: